#include <stdlib.h>
#include <string.h>

#include "rarray.h"
#include "rstring.h"
#include "parser_unref.h"

/* language structures */

void unref_if(if_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->expr);
		unref_statement(self->pos);
		unref_statement(self->neg);
		free(self);
	}
}

void unref_for(for_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->condition);
		unref_statement(self->init);
		unref_statement(self->step);
		unref_statement(self->body);
		free(self);
	}
}

void unref_try(try_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_statement(self->body);
		unref_statement(self->finally);
		unref_statement(self->generic);
		rarray_unref(&(self->excepts));
		free(self);
	}
}

void unref_case(case_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		rarray_unref(&(self->ranges));
		unref_statement(self->body);
		free(self);
	}
}

void unref_while(while_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->expr);
		unref_statement(self->body);
		free(self);
	}
}

void unref_except(except_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_statement(self->body);
		rarray_unref(&(self->types));
		rstring_release(&(self->name));
		free(self);
	}
}

void unref_switch(switch_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->expr);
		unref_statement(self->other);
		rarray_unref(&(self->cases));
		free(self);
	}
}

void unref_compond(compond_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		rarray_unref(&(self->statements));
		free(self);
	}
}

void unref_function(function_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_statement(self->body);
		rarray_unref(&(self->args));
		rstring_release(&(self->name));
		rstring_release(&(self->vararg));
		free(self);
	}
}

/* statements */

void unref_assign(assign_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case tv_attr:	unref_attr(self->tv_attr);		break;
			case tv_name:	unref_name(self->tv_name);		break;
			case tv_index:	unref_index(self->tv_index);	break;
		}

		unref_expr(self->expr);
		free(self);
	}
}

void unref_delete(delete_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case tv_attr:	unref_attr(self->tv_attr);		break;
			case tv_name:	unref_name(self->tv_name);		break;
			case tv_index:	unref_index(self->tv_index);	break;
		}

		free(self);
	}
}

void unref_augment(augment_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case tv_attr:	unref_attr(self->tv_attr);		break;
			case tv_name:	unref_name(self->tv_name);		break;
			case tv_index:	unref_index(self->tv_index);	break;
		}

		unref_expr(self->expr);
		rstring_release(&(self->operator));
		free(self);
	}
}

/* control flows */

void unref_break(break_t *self)
{
	if (self && --(self->node.ref) == 0)
		free(self);
}

void unref_return(return_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->expr);
		free(self);
	}
}

void unref_continue(continue_t *self)
{
	if (self && --(self->node.ref) == 0)
		free(self);
}

/* generic statement */

void unref_statement(statement_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case st_if:			unref_if(self->st_if);				break;
			case st_for:		unref_for(self->st_for);			break;
			case st_try:		unref_try(self->st_try);			break;
			case st_while:		unref_while(self->st_while);		break;
			case st_switch:		unref_switch(self->st_switch);		break;
			case st_compond:	unref_compond(self->st_compond);	break;
			case st_function:	unref_function(self->st_function);	break;
			case st_assign:		unref_assign(self->st_assign);		break;
			case st_delete:		unref_delete(self->st_delete);		break;
			case st_invoke:		unref_invoke(self->st_invoke);		break;
			case st_augment:	unref_augment(self->st_augment);	break;
			case st_break:		unref_break(self->st_break);		break;
			case st_return:		unref_return(self->st_return);		break;
			case st_continue:	unref_continue(self->st_continue);	break;
		}

		free(self);
	}
}

/* assign targets */

void unref_name(name_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		rstring_release(&(self->name));
		free(self);
	}
}

void unref_attr(attr_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_rvalue(self->value);
		rstring_release(&(self->attribute));
		free(self);
	}
}

void unref_index(index_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->expr);
		unref_rvalue(self->value);
		free(self);
	}
}

void unref_invoke(invoke_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_rvalue(self->value);
		rarray_unref(&(self->args));
		rarray_unref(&(self->expand));
		free(self);
	}
}

void unref_rvalue(rvalue_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case rv_name:	unref_name(self->rv_name);		break;
			case rv_attr:	unref_attr(self->rv_attr);		break;
			case rv_unit:	unref_unit(self->rv_unit);		break;
			case rv_const:	unref_const(self->rv_const);	break;
			case rv_index:	unref_index(self->rv_index);	break;
			case rv_invoke:	unref_invoke(self->rv_invoke);	break;
		}

		free(self);
	}
}

/* expressions */

void unref_expr(expr_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->el_type)
		{
			case e_expr:	unref_expr(self->el_expr);		break;
			case e_rvalue:	unref_rvalue(self->el_rvalue);	break;
		}

		switch (self->er_type)
		{
			case e_expr:	unref_expr(self->er_expr);		break;
			case e_rvalue:	unref_rvalue(self->er_rvalue);	break;
		}

		rstring_release(&(self->operator));
		free(self);
	}
}

void unref_unit(unit_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case u_expr:		unref_expr(self->u_expr);			break;
			case u_unit:		unref_unit(self->u_unit);			break;
			case u_rvalue:		unref_rvalue(self->u_rvalue);		break;
			case u_function:	unref_function(self->u_function);	break;
		}

		rstring_release(&(self->operator));
		free(self);
	}
}

void unref_const(const_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		switch (self->type)
		{
			case c_int:											break;
			case c_float:										break;
			case c_string:	rstring_release(&(self->c_string));	break;
		}

		free(self);
	}
}

void unref_range(range_t *self)
{
	if (self && --(self->node.ref) == 0)
	{
		unref_expr(self->end);
		unref_expr(self->begin);
		free(self);
	}
}
